package com.smilive.start.netty.jdk;

import com.smilive.start.netty.utils.ByteBufferUtil;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;

/**
 * @program: netty_start
 * @Date: 2022-01-04 14:46
 * @Author: smilive
 * @Description: Write事件
 */
@Slf4j
public class NetSelectorServer02 {

    public static void main(String[] args) {

        try (ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()){
            serverSocketChannel.bind(new InetSocketAddress(8000));
            serverSocketChannel.configureBlocking(false);
            //创建选择器
            Selector selector = Selector.open();

            //将通道注册到选择器,并设置监听事件
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

            while (true){
                // 若没有事件就绪，线程会被阻塞，反之不会被阻塞。从而避免了CPU空转
                // 返回值为就绪的事件个数
                int ready = selector.select();
                //获取所有监听事件
                Set<SelectionKey> selectionKeys = selector.selectedKeys();

                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()){
                    SelectionKey selectionKey = iterator.next();

                    //判断可以类型
                    if (selectionKey.isAcceptable()) {
                        // 获得key对应的channel
                        ServerSocketChannel channel = (ServerSocketChannel)selectionKey.channel();
                        SocketChannel socketChannel = channel.accept();
                        log.debug("建立连接:{}",socketChannel);
                        // 设置为非阻塞模式，
                        socketChannel.configureBlocking(false);
                        // 写入数据
                        StringBuilder builder = new StringBuilder();
                        for(int i = 0; i < 50000000; i++) {
                            builder.append("a");
                        }
                        // 先执行一次Buffer->Channel的写入，如果未写完，就添加一个可写事件
                        ByteBuffer buffer = StandardCharsets.UTF_8.encode(builder.toString());
                        int write = socketChannel.write(buffer);
                        System.out.println(write);
                        if (buffer.hasRemaining()){
                            //将连接的通道也注册到选择其中
                            socketChannel.register(selector,SelectionKey.OP_WRITE,buffer);
                        }
                        iterator.remove();
                    }
                    if (selectionKey.isWritable()){
                        // 获得key对应的channel
                        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                        //通过key 获取附件
                        ByteBuffer byteBuffer = (ByteBuffer) selectionKey.attachment();
                        int write = socketChannel.write(byteBuffer);
                        System.out.println(write);
                        // 如果已经完成了写操作，需要移除key中的附件，同时不再对写事件感兴趣
                        if (!byteBuffer.hasRemaining()) {
                            selectionKey.attach(null);
                            selectionKey.interestOps(0);
                        }
                    }
                }

            }


        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void split(ByteBuffer buffer) {
        buffer.flip();
        for(int i = 0; i < buffer.limit(); i++) {
            // 遍历寻找分隔符
            // get(i)不会移动position
            if (buffer.get(i) == '\n') {
                // 缓冲区长度
                int length = i+1-buffer.position();
                ByteBuffer target = ByteBuffer.allocate(length);
                // 将前面的内容写入target缓冲区
                for(int j = 0; j < length; j++) {
                    // 将buffer中的数据写入target中
                    target.put(buffer.get());
                }
                // 打印结果
                ByteBufferUtil.debugAll(target);
            }
        }
        // 切换为写模式，但是缓冲区可能未读完，这里需要使用compact
        buffer.compact();
    }
}
